package de.wiroo.setup.web.common;

import java.util.Calendar;
import java.util.Date;
import java.util.List;

import org.apache.wicket.Component;
import org.apache.wicket.MarkupContainer;
import org.apache.wicket.datetime.markup.html.basic.DateLabel;
import org.apache.wicket.event.IEvent;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.EnumLabel;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.markup.html.panel.Fragment;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.AbstractReadOnlyModel;
import org.apache.wicket.model.CompoundPropertyModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.ResourceModel;
import org.springframework.format.annotation.DateTimeFormat;

import de.stefanocke.roo.wicket.RooFactoryMethod;
import de.stefanocke.roo.wicket.RooStereotype;

@RooStereotype("wicket.Details")
public abstract class DetailsPanel<T> extends Panel implements ILabeled {

	/***/
	private static final long serialVersionUID = 1L;

	/**
	 * The ID for the container providing the CPM.
	 */
	private static final String CPM_CONTAINER_ID = "cpm";

	/**
	 * The ID for the label component.
	 */
	private static final String LABEL_ID = "label";

	protected DetailsPanel() {
		super("INVALID");
		throw new UnsupportedOperationException(
				"Do not use this constructor since it is only a workaround for ITD issues");
	}

	@Deprecated
	public DetailsPanel(String id) {
		super(id);
		init();
	}

	@Deprecated
	public DetailsPanel(String id, IModel<T> model) {
		super(id, new CompoundPropertyModel<T>(model));
		init();
	}

	public DetailsPanel(String id, IModel<T> model, IServiceBindings<T, ?> bindings) {
		super(id, new CompoundPropertyModel<T>(model != null ? model : bindings.createModel(null) )); //TODO...
		init();
	}

	
	protected void init() {
		WebMarkupContainer cpmContainer = new WebMarkupContainer(CPM_CONTAINER_ID);
		Label label = new Label(LABEL_ID, new ResourceModel(getId(), "")); //TODO
		cpmContainer.add(label);
		List<Component> fields = createFields();
		cpmContainer.add(fields.toArray(new Component[0]));
		add(cpmContainer);
		setOutputMarkupId(true);

	}

	@Override
	public void setLabel(IModel<String> labelModel) {
		getLabelComponent().setDefaultModel(labelModel);
	}

	private Label getLabelComponent() {
		return (Label) getCpmContainer().get(LABEL_ID);
	}

	@SuppressWarnings("unchecked")
	@Override
	public IModel<String> getLabel() {
		return (IModel<String>) getLabelComponent().getDefaultModel();
	}

	@Override
	protected void onInitialize() {
		super.onInitialize();

		onModelChanged();
	}

	@SuppressWarnings("unchecked")
	@Override
	protected void onModelChanged() {
		IModel<T> model = (IModel<T>) getDefaultModel();
		getCpmContainer().setDefaultModel(new CompoundPropertyModel<T>(model));
		super.onModelChanged();
	}

	/**
	 * The CPM Container is the container that provides the {@link CompoundPropertyModel}.
	 * 
	 * @return the CPM container.
	 */
	public WebMarkupContainer getCpmContainer() {
		return (WebMarkupContainer) get(CPM_CONTAINER_ID);
	}

	protected abstract List<Component> createFields();

	/**
	 * Gets model object
	 * 
	 * @return model object
	 */
	@SuppressWarnings("unchecked")
	public final T getModelObject() {
		return (T) getDefaultModelObject();
	}

	/**
	 * Gets model
	 * 
	 * @return model
	 */
	@SuppressWarnings("unchecked")
	public final CompoundPropertyModel<T> getModel() {
		return (CompoundPropertyModel<T>) getDefaultModel();
	}

	@RooStereotype("wicket.ListView")
	public static abstract class ListField<T> extends ComplexField {

		/***/
		private static final long serialVersionUID = 1L;

		public ListField(String id, MarkupContainer markupProvider) {
			super(id, markupProvider);
		}

		@Override
		protected Component createComponent(String id, MarkupContainer markupProvider) {
			return new ListViewFragment<T>(id, markupProvider) {

				/***/
				private static final long serialVersionUID = 1L;

				@Override
				protected Component createItemComponent(String id) {
					return ListField.this.createItemComponent(id);
				}

				protected String getItemLabel(final ListItem<T> item) {
					// TODO: It is very implicit to assume the parent provides
					// the item
					// label...
					return getLocalizer().getString(ListField.this.getId(), ListField.this) + " "
							+ (item.getIndex() + 1);
				}

				@Override
				protected IModel<String> getItemLabelModel(final ListItem<T> item) {

					return new AbstractReadOnlyModel<String>() {

						/***/
						private static final long serialVersionUID = 1L;

						@Override
						public String getObject() {
							return getItemLabel(item);
						}

					};

				}

			};
		}

		protected abstract Component createItemComponent(String id);

	}

	public static abstract class ListViewFragment<T> extends Fragment {

		/***/
		private static final long serialVersionUID = 1L;

		public ListViewFragment(String id, MarkupContainer markupProvider) {
			super(id, "listFragment", markupProvider);
			add(new ListView<T>("list") {

				/***/
				private static final long serialVersionUID = 1L;

				@Override
				protected void populateItem(ListItem<T> item) {
					Component itemComponent = createItemComponent("item");
					itemComponent.setDefaultModel(item.getModel());
					if (itemComponent instanceof ILabeled) {
						((ILabeled) itemComponent).setLabel(getItemLabelModel(item));
					}

					item.add(itemComponent);
				}

			});
		}

		protected abstract IModel<String> getItemLabelModel(ListItem<T> item);

		@Override
		protected void onInitialize() {
			super.onInitialize();
			onModelChanged();
		}

		@Override
		protected void onModelChanged() {
			super.onModelChanged();
			get("list").setDefaultModel(getDefaultModel());
		}

		protected abstract Component createItemComponent(String id);

	}

	public static abstract class SimpleField extends DecoratorFragment<Component> {

		/***/
		private static final long serialVersionUID = 1L;

		public SimpleField(String id, MarkupContainer markupProvider) {
			super(id, "simpleField", markupProvider);
		}

	}

	public static abstract class ComplexField extends DecoratorFragment<Component> {

		/***/
		private static final long serialVersionUID = 1L;

		public ComplexField(String id, MarkupContainer markupProvider) {
			super(id, "complexField", markupProvider);
		}
		
		public ComplexField(String id, MarkupContainer markupProvider, boolean inList) {
			super(id, "complexField", markupProvider);
		}

	}

	@RooFactoryMethod(modelClasses = { Calendar.class, Date.class })
	protected SimpleField createDateLabelDecorated(String id, final DateTimeFormat format) {
		return new SimpleField(id, this) {

			/***/
			private static final long serialVersionUID = 1L;

			@Override
			protected Component createComponent(String id) {
				return createDateLabel(id, format);
			}
		};
	}

	protected DateLabel createDateLabel(String id, DateTimeFormat format) {
		if (format != null) {
			if (!format.pattern().isEmpty()) {
				return DateLabel.forDatePattern(id, format.pattern());
			} else {
				return DateLabel.forDateStyle(id, format.style());
			}
		} else {
			return DateLabel.forDateStyle(id, "SS");
		}
	}

	@RooFactoryMethod(modelClasses = Enum.class)
	protected SimpleField createEnumLabelDecorated(String id) {
		return new SimpleField(id, this) {

			/***/
			private static final long serialVersionUID = 1L;

			@Override
			protected Component createComponent(String id) {
				return createEnumLabel(id);
			}
		};
	}

	protected <E extends Enum<E>> EnumLabel<E> createEnumLabel(String id) {
		return new EnumLabel<E>(id);
	}

	// @RooFactoryMethod(modelClasses = Boolean.class)
	// protected Label createBooleanLabel(String id) {
	// return new Label(id);
	// }

	@RooFactoryMethod
	protected SimpleField createStringLabelDecorated(String id) {
		return new SimpleField(id, this) {

			/***/
			private static final long serialVersionUID = 1L;

			@Override
			protected Component createComponent(String id) {
				return createStringLabel(id);
			}
		};
	}

	protected Label createStringLabel(String id) {
		return new Label(id);
	}
	
	/**
	 * Set the visibility of the content of the panel but let the panel itself visible. Useful for Ajax updates, since it allows 
	 * to simply add the DetailsPanel to the AjaxRequestTarget instead of adding on more div around it.
	 * @param visible
	 */
	public void setInnerVisible(boolean visible){
		getCpmContainer().setVisible(visible);
	}
	
	@Override
	public void onEvent(IEvent<?> event) {
		if(event.getPayload() instanceof NavigationEvent<?>){
			NavigationEvent<?> ne = (NavigationEvent<?>) event.getPayload();	
			onNavigationEvent(ne);
			event.dontBroadcastDeeper();
		} else if(event.getPayload() instanceof ActionEvent<?, ?>){
			ActionEvent<?, ?> ae = (ActionEvent<?, ?>) event.getPayload();
			onActionEvent(ae);
			event.dontBroadcastDeeper();
		}
	}

	protected void onActionEvent(ActionEvent<?, ?> ae) {
		
		
	}

	protected void onNavigationEvent(NavigationEvent<?> ne) {
		if(NavigationEvent.DETAILS == ne.getKind() && isMatchingObjectClass(ne.getModel())){
			
			this.setDefaultModel(ne.getModel());
			this.setInnerVisible(true);
			if(ne.getTarget() != null){
				ne.getTarget().add(this);
			}
			
		}
		//Shall we hide ourselves on other navigation events?
	}
	
	protected boolean isMatchingObjectClass(IModel<?> model) {
		Object object = model.getObject();
		if(object != null && object.getClass() == getObjectClass()){
			//TODO: Support instenace of?
			return true;
		} else {
			return false;
		}
	}

	protected abstract Class<T> getObjectClass();
}


